#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>

#include "m_ring_fifo.h"

//对比
//cmp tmpfile/read_file tmpfile/write_file

pthread_t thd_rd[1], thd_wr[1];

static void* task_read(void *para);
static void* task_write(void *para);
static uint32_t get_tick_count(void);

struct m_ring_fifo_t *m_ring_fifo;

uint8_t buf[1u << 30];

#define MIN_TEST_DATA_SIZE    0
#define MAX_TEST_DATA_SIZE    34567
//测试数据长度
#define WR_TEST_DATA_SIZE (rand() % (MAX_TEST_DATA_SIZE - MIN_TEST_DATA_SIZE + 1) + MIN_TEST_DATA_SIZE)

#define MIN_WRITE_TIMERS    15000
#define MAX_WRITE_TIMERS    15001
//写次数
#define WR_TIMERS (rand() % (MIN_WRITE_TIMERS - MIN_WRITE_TIMERS + 1) + MIN_WRITE_TIMERS)

FILE *f_write, *f_read;

uint32_t all_write_success;

int main(int argc, char *argv[])
{
    /* 创建并打开写文件 */
    f_write = fopen("tmpfile/write_file", "w+");
    if(!f_write)
    {
        perror("fopen write_file");

        return EXIT_FAILURE;
    }
    /* 创建并打开读文件 */
    f_read = fopen("tmpfile/read_file", "w+");
    if(!f_read)
    {
        perror("fopen read_file");

        return EXIT_FAILURE;
    }

    /* 初始化环形缓冲区 */
    m_ring_fifo = m_ring_fifo_init(NULL, sizeof(buf));
    if(NULL == m_ring_fifo)
    {
        printf("m_ring_fifo_init error\r\n");

        return EXIT_FAILURE;
    }

    /* 根据线程句柄数创建读线程 */
    for(uint32_t i = 0;i < sizeof(thd_rd) / sizeof(*thd_rd);i++)
    {
        pthread_create(&thd_rd[i], NULL, task_read, NULL);
    }

    /* 根据线程句柄数创建写线程 */
    for(uint32_t i = 0;i < sizeof(thd_wr) / sizeof(*thd_wr);i++)
    {
        pthread_create(&thd_wr[i], NULL, task_write, NULL);
    }

    /* 等待所有写线程结束 */
    for(uint32_t i = 0;i < sizeof(thd_wr) / sizeof(*thd_wr);i++)
    {
        pthread_join(thd_wr[i], NULL);
    }
    /* 标记所有写线程已结束 */
    all_write_success = 1;

    /* 等待所有读线程结束 */
    for(uint32_t i = 0;i < sizeof(thd_rd) / sizeof(*thd_rd);i++)
    {
        pthread_join(thd_rd[i], NULL);
    }

    fclose(f_write);
    fclose(f_read);
    m_ring_fifo_destroy(m_ring_fifo);

    return 0;
}

static uint64_t read_success_count;
void write_rd_file(const void *buf, uint32_t len)
{
    size_t size;
    uint32_t retry_cnt;

    if(0 == len)
    {
        return ;
    }

    read_success_count += len;
    retry_cnt = 0;
    do {
        size = fwrite(buf, len, 1, f_read);
        if(size >= 1)
        {
            break;
        }
    }while(++retry_cnt < 3);
    if(retry_cnt >= 3)
    {
        printf("read thread[%lu] write file failed..\r\n", pthread_self());
    }
}

void* task_read(void *para)
{
    uint32_t rd_len;
    uint8_t test_read_data[MAX_TEST_DATA_SIZE];

    printf("enter read thread[%lu]\n", pthread_self());

    for(;;)
    {
        if(sizeof(thd_rd) / sizeof(*thd_rd) > 1)
        {
            rd_len = m_ring_fifo_read_mc(m_ring_fifo, test_read_data, sizeof(test_read_data));
        }
        else
        {
            rd_len = m_ring_fifo_read_sc(m_ring_fifo, test_read_data, sizeof(test_read_data));

            write_rd_file(test_read_data, rd_len);
        }
        if((1 == all_write_success) && (0 == rd_len))
        {
            break;
        }

        //模拟数据处理
        // usleep(100);
    }

    printf("\r\nthread[%lu] read_success_count:%lu\r\n", pthread_self(), read_success_count);
    printf("thread[%lu] task_read exit..\r\n", pthread_self());

    return (void *)0;
}

static uint64_t write_success_count;
void write_wr_file(void *buf, uint32_t len)
{
    size_t size;
    uint32_t retry_cnt;

    if(0 == len)
    {
        return ;
    }

    write_success_count += len;
    retry_cnt = 0;
    do {
        size = fwrite(buf, len, 1, f_write);
        if(size >= 1)
        {
            break;
        }
    }while(++retry_cnt < 3);
    if(retry_cnt >= 3)
    {
        printf("write thread[%lu] write file failed..\r\n", pthread_self());
    }
}

void* task_write(void *para)
{
    uint32_t wr_len, real_wr_len;
    uint8_t test_write_data[MAX_TEST_DATA_SIZE];

    /* 填充随机数据 */
    for(uint32_t i = 0;i < sizeof(test_write_data);i++)
    {
        test_write_data[i] = rand() % (0x39 - 0x30 + 1) + 0x30;
        usleep(1000);
    }

    srand(get_tick_count());
    for(uint32_t i = 0;i < WR_TIMERS;i++)
    {
        srand(get_tick_count());
        wr_len = WR_TEST_DATA_SIZE;
        if(sizeof(thd_wr) / sizeof(*thd_wr) > 1)
        {
            real_wr_len = m_ring_fifo_write_mp(m_ring_fifo, test_write_data, wr_len);
        }
        else
        {
            real_wr_len = m_ring_fifo_write_sp(m_ring_fifo, test_write_data, wr_len);

            write_wr_file(test_write_data, real_wr_len);
        }

        if(real_wr_len != wr_len)
        {
            // printf("fifo is full.\n");
        }
        if(0 == real_wr_len)
        {
            continue;
        }

        //模拟数据接收
        // usleep(1);
    }

    printf("\r\nthread[%lu] write_success_count:%lu\r\n", pthread_self(), write_success_count);
    printf("thread[%lu] task_write exit..\r\n", pthread_self());

    sleep(1);

    return (void *)0;
}

uint32_t get_tick_count(void)
{
    struct timespec tp;

    clock_gettime(CLOCK_MONOTONIC, &tp);

    return tp.tv_nsec;
}
